通用函数调用语法

ufcs.md


commit ccb1d87d6faa9ff528d22b96595a0e2cbb16c0f2

有时,函数可能有相同的名字。就像下面这些代码:

  1. trait Foo {
  2. fn f(&self);
  3. }
  4. trait Bar {
  5. fn f(&self);
  6. }
  7. struct Baz;
  8. impl Foo for Baz {
  9. fn f(&self) { println!("Baz’s impl of Foo"); }
  10. }
  11. impl Bar for Baz {
  12. fn f(&self) { println!("Baz’s impl of Bar"); }
  13. }
  14. let b = Baz;

如果我们尝试调用b.f(),我们会得到一个错误:

  1. error: multiple applicable methods in scope [E0034]
  2. b.f();
  3. ^~~
  4. note: candidate #1 is defined in an impl of the trait `main::Foo` for the type
  5. `main::Baz`
  6. fn f(&self) { println!("Baz’s impl of Foo"); }
  7. ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  8. note: candidate #2 is defined in an impl of the trait `main::Bar` for the type
  9. `main::Baz`
  10. fn f(&self) { println!("Baz’s impl of Bar"); }
  11. ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

我们需要一个区分我们需要调用哪一函数的方法。这个功能叫做“通用函数调用语法”(universal function call syntax),这看起来像这样:

  1. # trait Foo {
  2. # fn f(&self);
  3. # }
  4. # trait Bar {
  5. # fn f(&self);
  6. # }
  7. # struct Baz;
  8. # impl Foo for Baz {
  9. # fn f(&self) { println!("Baz’s impl of Foo"); }
  10. # }
  11. # impl Bar for Baz {
  12. # fn f(&self) { println!("Baz’s impl of Bar"); }
  13. # }
  14. # let b = Baz;
  15. Foo::f(&b);
  16. Bar::f(&b);

让我们拆开来看。

  1. Foo::
  2. Bar::

调用的这一半是两个 trait 的类型:FooBar。这样实际上就区分了这两者:Rust 调用你使用的 trait 里面的方法。

  1. f(&b)

当我们使用方法语法调用像b.f()这样的方法时,如果f()需要&self,Rust 实际上会自动地把b借用为&self。而在这个例子中,Rust 并不会这么做,所以我们需要显式地传递一个&b

尖括号形式(Angle-bracket Form)

我们刚才讨论的通用函数调用语法的形式:

  1. Trait::method(args);

上面的形式其实是一种缩写。这是在一些情况下需要使用的扩展形式:

  1. <Type as Trait>::method(args);

<>::语法是一个提供类型提示的方法。类型位于<>中。在这个例子中,类型是Type as Trait,表示我们想要methodTrait版本被调用。在没有二义时as Trait部分是可选的。尖括号也是一样。因此上面的形式就是一种缩写的形式。

这是一个使用较长形式的例子。

  1. trait Foo {
  2. fn foo() -> i32;
  3. }
  4. struct Bar;
  5. impl Bar {
  6. fn foo() -> i32 {
  7. 20
  8. }
  9. }
  10. impl Foo for Bar {
  11. fn foo() -> i32 {
  12. 10
  13. }
  14. }
  15. fn main() {
  16. assert_eq!(10, <Bar as Foo>::foo());
  17. assert_eq!(20, Bar::foo());
  18. }

使用尖括号语法让你可以调用指定 trait 的方法而不是继承到的那个。